Skip to content

feat(compass): handle-address request inputs — schema, proto flip, resolver, comms edge (RIG-2880) - #698

Merged
mattwilkinsonn merged 4 commits into
mainfrom
compass-server/rig-2880-handle-cutover-foundation
Aug 29, 2026
Merged

feat(compass): handle-address request inputs — schema, proto flip, resolver, comms edge (RIG-2880)#698
mattwilkinsonn merged 4 commits into
mainfrom
compass-server/rig-2880-handle-cutover-foundation

Conversation

@rigel-mintaka

Copy link
Copy Markdown
Contributor

The handle-addressing cutover: every request-input account field carries a @handle, resolved to an account id at the service edge, per docs/designs/product/compass-handle-addressing-cutover/design.md (Plan T0-T3, plus the compass-agent T6 tool rewire folded in to keep the tree green).

T0 — resolution index (0001_init.sql). Adds account_handles(account_id, handle, owner_user_id NULL) with two partial-unique indexes: user/system handles globally unique (owner_user_id IS NULL), agent handles unique per owner. accounts.handle stays as a display column, no longer the resolution key. No backfill (pre-dogfood); store write paths insert the handle row in the same tx as the account.

T1 — proto flip (comms.proto, agent_gateway.proto, compass.proto). Every request-input account field renamed to its @handle form (member_handles, add/remove/subscribe/unsubscribe_handles, agent_handle, new_parent_handle, parent_handle, owner_handle, vantage_handle, account_handle), field numbers kept in place (rename-in-place, DL-186). Response, stored, and event account fields stay id-typed (the stable join keys clients hold). All four gen lanes regenerated; gen-fence clean.

T2 — resolver (accounts.go). AccountsByHandles is an owner-qualified batch resolve over account_handles: atomic (any miss fails the whole call, naming every unresolved handle in submitted spelling — OQ-2), visibility-scoped (an invisible handle misses exactly like an unknown one — OQ-6), system-excluded. AgentByHandle re-keyed to (owner, handle); UserByHandle global-tier helper; QualifiedHandle/ParseQualifiedHandle split on the first /.

T3 — comms edge (resolve.go new, mapping.go, roster.go). Handles resolve before the store calls, with oracle-safe NOT_FOUND remaps on the authority/visibility legs (DL-269): CreateAgent foreign parent, ReparentAgent ErrPermissionDenied merge, OpenAgentWorkspace re-key to the submitted handle, roster vantage visibility-close. A delivery mention @handle resolves in the posting author's owner namespace. memberUpdatesFromWire merges post-resolution so two spellings of one handle cannot yield two conflicting MemberUpdates.

T6 (folded) — compass-agent despawn tool (lifecycle.ts). agents_despawn_peer takes agent_handle (non-blank) and builds DespawnPeerRequest{agentHandle}; the description drops "by its agent account id". The roster tool is unchanged (it never set the vantage). Folded into this PR so the whole tree — Go, proto, and the bun lane the proto regen touches — stays green in one atomic change rather than landing a red intermediate.

Admin/ops (T8) and lifecycle despawn (T4) server handlers are mechanically renamed to the handle-typed fields but still consume the value as an id (documented id-passthrough skew); their real resolution lands in the follow-up T4/T8 commits on this stack.

Verification. go vet ./... and -tags "pgtest podman unix microvm" clean, go build ./... clean, all test binaries compile, compass-proto:drift/gen-fence pass, store/comms/delivery pgtests pass against live Postgres, go test -race clean; compass-agent typecheck + 610 bun tests green; compass-ui/compass-client typecheck green.

Adds DL-269 (request-input account fields are handle-typed), DL-270 (response/stored/event fields stay id-typed), DL-271 (owner-namespaced account_handles resolution) on merge.

Unblocks the remaining cutover lanes: T5 rebase of #628/#630, T8 admin/ops resolution, T9 additive Message.author_handle, and the compass-ui T7 sweep.

Spec-impact: none. Refs RIG-2880

Co-authored-by: Matt Wilkinson matt@rigel.build

@linear-code

linear-code Bot commented Aug 28, 2026

Copy link
Copy Markdown

RIG-2880

@github-actions

github-actions Bot commented Aug 28, 2026

Copy link
Copy Markdown

Compass engineering docs preview: https://compass-server-rig-2880-hand.compass-eng-docs.pages.dev

Deployed from compass-server/rig-2880-handle-cutover-foundation at 61f85c6.

Changed pages:

rigel-mintaka added a commit that referenced this pull request Aug 28, 2026
…path (RIG-2880)

Review-fix on #698 (low, non-gating). The re-review noted that comms.ReparentAgent's new edge pre-check makes the store's validateNewParent same-owner ErrPermissionDenied unreachable via the RPC path — a future reader at the store clause might mistake it for dead code. Adds a one-line note that it is edge-shadowed on the RPC path and remains as store-layer defense-in-depth (the store is independently callable and tested).

Co-authored-by: Matt Wilkinson <matt@rigel.build>
rigel-mintaka added a commit that referenced this pull request Aug 28, 2026
…path (RIG-2880)

Review-fix on #698 (low, non-gating). The re-review noted that comms.ReparentAgent's new edge pre-check makes the store's validateNewParent same-owner ErrPermissionDenied unreachable via the RPC path — a future reader at the store clause might mistake it for dead code. Adds a one-line note that it is edge-shadowed on the RPC path and remains as store-layer defense-in-depth (the store is independently callable and tested).

Co-authored-by: Matt Wilkinson <matt@rigel.build>
@rigel-mintaka
rigel-mintaka force-pushed the compass-server/rig-2880-handle-cutover-foundation branch from 241ec9b to 8dc76ab Compare August 28, 2026 03:24
@rigel-mintaka

Copy link
Copy Markdown
Contributor Author

Review loop complete (self-driven review agent, two rounds) — all-clear at the high+medium floor.

Round 1 (ReviewPR698Handle): 1 high, 1 medium, 2 low.

  • HIGH — ReparentAgent oracle leak (DL-269): a foreign new_parent_handle (an agent under another owner) returned NOT_FOUND naming the agent handle via the store's clause-1 remap, while an unknown parent named the parent handle — distinguishable, an enumeration probe. Fixed (comms.go): oracle-safe same-owner edge pre-check mirroring CreateAgent, naming the submitted new_parent_handle before the store call.
  • MEDIUM — reparent tests asserted code only. Fixed: TestReparentAgentCrossOwnerParentNotFound now asserts byte-identity modulo the caller's own submitted spelling (foreign names the parent handle, never "a").
  • LOW-1 — memberUpdatesFromWire named only the first failing list's misses (OQ-2). Fixed: one-pass combined resolveHandles over all four lists + new cross-list test.
  • LOW-2 — global user-directory visibility (accountVisibleFromWhere OR u.account_id IS NOT NULL): every user account is visible to every caller. Not fixed — pre-existing and ratified (the id-path had identical visibility; DL-271 makes user handles globally unique by design; OQ-6 ruled SCOPED, comment at accounts.go:940-952). The adjacent agent-sibling widening is already filed as RIG-2858. Surfaced, not blocking.

Round 2 (ReviewPR698Fix): all four round-1 findings CLOSED, 0 high / 0 medium — all-clear. LOW-2 dispositioned as above; a store-clause edge-shadow comment (accounts.go) added for the future reader.

Adds DL-269/270/271 to the ledger (rebase-merged with #695's DL-281). mergeable=MERGEABLE; holding at your gate.

rigel-mintaka added a commit that referenced this pull request Aug 28, 2026
…path (RIG-2880)

Review-fix on #698 (low, non-gating). The re-review noted that comms.ReparentAgent's new edge pre-check makes the store's validateNewParent same-owner ErrPermissionDenied unreachable via the RPC path — a future reader at the store clause might mistake it for dead code. Adds a one-line note that it is edge-shadowed on the RPC path and remains as store-layer defense-in-depth (the store is independently callable and tested).

Co-authored-by: Matt Wilkinson <matt@rigel.build>
@rigel-mintaka
rigel-mintaka force-pushed the compass-server/rig-2880-handle-cutover-foundation branch from 8dc76ab to 139ecdb Compare August 28, 2026 03:45
rigel-mintaka and others added 4 commits August 28, 2026 01:34
…solver, comms edge (RIG-2880)

The handle-addressing cutover: every request-input account field carries a `@handle`, resolved to an account id at the service edge, per `docs/designs/product/compass-handle-addressing-cutover/design.md` (Plan T0-T3, plus the compass-agent T6 tool rewire folded in to keep the tree green).

**T0 — resolution index** (`0001_init.sql`). Adds `account_handles(account_id, handle, owner_user_id NULL)` with two partial-unique indexes: user/system handles globally unique (`owner_user_id IS NULL`), agent handles unique per owner. `accounts.handle` stays as a display column, no longer the resolution key. No backfill (pre-dogfood); store write paths insert the handle row in the same tx as the account.

**T1 — proto flip** (`comms.proto`, `agent_gateway.proto`, `compass.proto`). Every request-input account field renamed to its `@handle` form (`member_handles`, `add/remove/subscribe/unsubscribe_handles`, `agent_handle`, `new_parent_handle`, `parent_handle`, `owner_handle`, `vantage_handle`, `account_handle`), field numbers kept in place (rename-in-place, DL-186). Response, stored, and event account fields stay id-typed (the stable join keys clients hold). All four gen lanes regenerated; gen-fence clean.

**T2 — resolver** (`accounts.go`). `AccountsByHandles` is an owner-qualified batch resolve over `account_handles`: atomic (any miss fails the whole call, naming every unresolved handle in submitted spelling — OQ-2), visibility-scoped (an invisible handle misses exactly like an unknown one — OQ-6), system-excluded. `AgentByHandle` re-keyed to `(owner, handle)`; `UserByHandle` global-tier helper; `QualifiedHandle`/`ParseQualifiedHandle` split on the first `/`.

**T3 — comms edge** (`resolve.go` new, `mapping.go`, `roster.go`). Handles resolve before the store calls, with oracle-safe NOT_FOUND remaps on the authority/visibility legs (DL-269): CreateAgent foreign parent, ReparentAgent `ErrPermissionDenied` merge, OpenAgentWorkspace re-key to the submitted handle, roster vantage visibility-close. A delivery mention `@handle` resolves in the posting author's owner namespace. `memberUpdatesFromWire` merges post-resolution so two spellings of one handle cannot yield two conflicting `MemberUpdate`s.

**T6 (folded) — compass-agent despawn tool** (`lifecycle.ts`). `agents_despawn_peer` takes `agent_handle` (non-blank) and builds `DespawnPeerRequest{agentHandle}`; the description drops "by its agent account id". The roster tool is unchanged (it never set the vantage). Folded into this PR so the whole tree — Go, proto, and the bun lane the proto regen touches — stays green in one atomic change rather than landing a red intermediate.

Admin/ops (T8) and lifecycle despawn (T4) server handlers are mechanically renamed to the handle-typed fields but still consume the value as an id (documented id-passthrough skew); their real resolution lands in the follow-up T4/T8 commits on this stack.

**Verification.** `go vet ./...` and `-tags "pgtest podman unix microvm"` clean, `go build ./...` clean, all test binaries compile, `compass-proto:drift`/`gen-fence` pass, store/comms/delivery pgtests pass against live Postgres, `go test -race` clean; `compass-agent` typecheck + 610 bun tests green; `compass-ui`/`compass-client` typecheck green.

Adds DL-269 (request-input account fields are handle-typed), DL-270 (response/stored/event fields stay id-typed), DL-271 (owner-namespaced `account_handles` resolution) on merge.

Unblocks the remaining cutover lanes: T5 rebase of #628/#630, T8 admin/ops resolution, T9 additive `Message.author_handle`, and the compass-ui T7 sweep.

Spec-impact: none. Refs RIG-2880

Co-authored-by: Matt Wilkinson <matt@rigel.build>
…ing, add DL rows (RIG-2880)

Review-fix commit on the handle-addressing foundation. Addresses the review of the parent commit (three code findings plus the mandated ledger rows); the low-2 finding (globally-visible user directory) is a pre-existing decision surfaced to Matt, not changed here.

HIGH — ReparentAgent oracle leak (DL-269 violation). A foreign new_parent_handle (one that exists under another owner) fell through the edge to the store, which re-keyed the error to name the AGENT handle, while an unknown parent named the submitted parent handle. The divergence let a caller distinguish "this parent exists under another owner" from "no such handle" — an enumeration probe against another owner's tree. Fixed by mirroring CreateAgent's same-owner edge pre-check in ReparentAgent (go/internal/comms/comms.go): after resolving the new parent, resolve the caller's owner and the parent's owner, and if they differ return the indistinguishable NOT_FOUND naming the submitted new_parent_handle. The store's clause-0 permission-denied catch stays correct for agent-authority; the store clause-1 foreign-parent path is now unreachable via the edge.

MEDIUM — reparent cross-owner tests asserted code only, not message. TestReparentAgentCrossOwnerParentNotFound now captures both the foreign-parent and unknown-parent errors and asserts each names its own submitted parent handle, that the foreign error does NOT name the agent handle, and that the two are byte-identical modulo the caller's own submitted spelling — the oracle invariant, not just the code.

LOW-1 — memberUpdatesFromWire OQ-2 completeness. The four member lists (add/subscribe/unsubscribe/remove) resolved sequentially, so a miss in a later list was masked by an earlier list's miss — the error named only the first failing list. Rewritten to one combined resolveHandles call over all four lists, slicing results back per-list by offset; every unresolved handle across every list is now named in one pass. New TestUpdateChannelMembersNamesMissesAcrossAllLists defends this.

Ledger — adds the three rows the frozen record's §Ledger impact mandates (DL-269 request-input handle-typing + oracle invariant, DL-270 response/stored/event stay id-typed, DL-271 owner-namespaced account_handles resolution), placed in the Storage section. The design PR merged without adding them; per house convention the impl PR fills its allocated numbers.

Verification: go build + go vet (all tags) clean; gofmt + compass-go:lint (0 issues) clean; comms pgtest suite green against live Postgres (reparent oracle fix, one-pass member resolution, and both new tests); design-ledger-gate green (244 rows, anchor resolves).

Co-authored-by: Matt Wilkinson <matt@rigel.build>
…path (RIG-2880)

Review-fix on #698 (low, non-gating). The re-review noted that comms.ReparentAgent's new edge pre-check makes the store's validateNewParent same-owner ErrPermissionDenied unreachable via the RPC path — a future reader at the store clause might mistake it for dead code. Adds a one-line note that it is edge-shadowed on the RPC path and remains as store-layer defense-in-depth (the store is independently callable and tested).

Co-authored-by: Matt Wilkinson <matt@rigel.build>
…st shadowing a human (RIG-2880)

The per-owner `account_handles` namespace (DL-271) changes the spawn-collision
domain from global to per-owner, which the two `go/server` lifecycle spawn tests
did not yet reflect — they asserted the pre-cutover global-unique behavior and
reddened the `pgtest` CI suite. Matt ruled the intended `SpawnPeer` contract
(2026-08-27): allow cross-owner same-name agents, but keep a defense-in-depth
guard so an agent can never shadow a human or the system sender.

- **Shadow guard** (`lifecycle.go`): `SpawnAsAccount` resolves the bare handle in
  the global user/system index (`UserByHandle`) before `CreateAgent`; a hit is
  the same in-band `already_exists`/`errHandleTaken` a duplicate agent handle
  gets, never revealing the account kind. Storage permits an agent handle to
  overlap a user/system handle (the two partial-unique indexes never contend),
  but resolution resolves the global tier first (OQ-7), so this closes the one
  way a peer could be created onto a human's handle.

- **`resumeOrReject` simplification** (`lifecycle.go`): with the guard excluding
  user/system handles and the per-owner namespace isolating foreign owners, a
  `CreateAgent` `ErrConflict` can only mean a same-owner agent already holds the
  handle. The two former reject branches (non-agent handle, foreign owner) are
  now unreachable and removed; an `AgentByHandle(callerOwner, handle)` miss there
  is an invariant violation (`CodeInternal`), not a routine collapse.

- **Tests** (`lifecycle_pgtest_test.go`): `...DifferentOwnerIsAlreadyExists` is
  rewritten as `...CreatesDistinctPeer` — owner B spawning owner A's handle now
  creates a distinct owner-B peer, A untouched, each owner's handle resolving to
  its own agent. `...CollidesWithUserAccountIsAlreadyExists` keeps its
  `already_exists` assertion, now backed by the guard, with an added
  system-handle sibling pinning the indistinguishable code shape.

- **Fake runner** (`service_placement_pgtest_test.go`): an opt-in FIFO
  container-name override (mirroring the existing `startIDs`), so the cross-owner
  test's two genuinely-distinct spawns get distinct container names instead of
  colliding on the fixture's constant `fakeContainer` (production derives the
  name from the globally-unique account id, so real spawns never collide).

Verification: `go build ./...`, `go vet -tags "pgtest podman unix microvm"`, and
the `go/server`, `go/internal/store`, `go/internal/comms` pgtest suites all green
against live Postgres. Self-driven `review` agent: 0 high / 0 medium / 2 low
(both no-change informational).

Refs RIG-2880

Co-authored-by: Matt Wilkinson <matt@rigel.build>
@rigel-mintaka
rigel-mintaka force-pushed the compass-server/rig-2880-handle-cutover-foundation branch from 139ecdb to 61f85c6 Compare August 28, 2026 05:36
@mattwilkinsonn
mattwilkinsonn merged commit 0a22bab into main Aug 29, 2026
13 checks passed
@mattwilkinsonn
mattwilkinsonn deleted the compass-server/rig-2880-handle-cutover-foundation branch August 29, 2026 19:15
rigel-mintaka added a commit that referenced this pull request Aug 29, 2026
Design record for agent↔agent peer DMs, built on a channel+topic **name-addressing cutover** that extends the merged handle-addressing cutover (#698 / RIG-2880) from accounts to channels+topics — plus the **RIG-2956 cross-channel reply-routing fix** as its T0 lane.

The dogfood "peer never replied" symptom was a misdiagnosis: forensics proved the peer took its turn and replied, but the reply landed in its own home channel because the steer op carries no source channel and `comms_post_message` defaults an omitted channel to home. This record fixes that and builds auto-created two-way DMs on top.

Folds Matt's rulings R1–R8 as frozen decisions: names not ids (ids are a leak); deliver/steer carry the source channel+topic and a reply names both explicitly (no home default); reserved DM namespace with a server-enforced create-guard; DM = exactly two parties, a third member converts it to a named channel; GROUP_DM retired reserve-not-delete; `create_topic` gate everywhere; DMs born `mandatory_subscription`; `comms_open_dm` + `comms_dm` direct-send + spawn-time auto-open. Only two non-load-bearing deferrals remain open (cross-owner DMs, UI DM naming).

Ledger-impact: deferred to freeze (DL-new-1..7 applied when this record merges; DECISIONS.md untouched in this PR).

Spec-impact: none

Refs RIG-2956

Co-authored-by: Matt Wilkinson <matt@rigel.build>
rigel-mintaka added a commit that referenced this pull request Aug 29, 2026
Design record for agent↔agent peer DMs, built on a channel+topic **name-addressing cutover** that extends the merged handle-addressing cutover (#698 / RIG-2880) from accounts to channels+topics — plus the **RIG-2956 cross-channel reply-routing fix** as its T0 lane.

The dogfood "peer never replied" symptom was a misdiagnosis: forensics proved the peer took its turn and replied, but the reply landed in its own home channel because the steer op carries no source channel and `comms_post_message` defaults an omitted channel to home. This record fixes that and builds auto-created two-way DMs on top.

Folds Matt's rulings R1–R8 as frozen decisions: names not ids (ids are a leak); deliver/steer carry the source channel+topic and a reply names both explicitly (no home default); reserved DM namespace with a server-enforced create-guard; DM = exactly two parties, a third member converts it to a named channel; GROUP_DM retired reserve-not-delete; `create_topic` gate everywhere; DMs born `mandatory_subscription`; `comms_open_dm` + `comms_dm` direct-send + spawn-time auto-open. Only two non-load-bearing deferrals remain open (cross-owner DMs, UI DM naming).

Ledger-impact: deferred to freeze (DL-new-1..7 applied when this record merges; DECISIONS.md untouched in this PR).

Spec-impact: none

Refs RIG-2956

Co-authored-by: Matt Wilkinson <matt@rigel.build>
mattwilkinsonn added a commit that referenced this pull request Aug 30, 2026
#726)

Design record for agent↔agent peer DMs, built on a channel+topic **name-addressing cutover** that extends the merged handle-addressing cutover (#698 / RIG-2880) from accounts to channels+topics — plus the **RIG-2956 cross-channel reply-routing fix** as its T0 lane.

The dogfood "peer never replied" symptom was a misdiagnosis: forensics proved the peer took its turn and replied, but the reply landed in its own home channel because the steer op carries no source channel and `comms_post_message` defaults an omitted channel to home. This record fixes that and builds auto-created two-way DMs on top.

Folds Matt's rulings R1–R8 as frozen decisions: names not ids (ids are a leak); deliver/steer carry the source channel+topic and a reply names both explicitly (no home default); reserved DM namespace with a server-enforced create-guard; DM = exactly two parties, a third member converts it to a named channel; GROUP_DM retired reserve-not-delete; `create_topic` gate everywhere; DMs born `mandatory_subscription`; `comms_open_dm` + `comms_dm` direct-send + spawn-time auto-open. Only two non-load-bearing deferrals remain open (cross-owner DMs, UI DM naming).

Ledger-impact: deferred to freeze (DL-new-1..7 applied when this record merges; DECISIONS.md untouched in this PR).

Spec-impact: none

Refs RIG-2956

Co-authored-by: Matt Wilkinson <matt@rigel.build>
rigel-mintaka added a commit that referenced this pull request Aug 30, 2026
…nels+topics by name (RIG-2956)

The T0 slice of the peer-DM name-addressing cutover (frozen record
docs/designs/product/compass-agent-peer-dm/design.md §T0). Agents and UIs
address channels and topics by NAME; the wire and store stay id-typed,
resolved at the service edge (mirrors the merged handle-addressing cutover
#698 / DL-270). Proto denorm fields + Go edge + delivery denorm + TS tools,
one atomic change.

Proto (regen committed):
- DeliverControl.channel_name=5, SteerControl.topic_name=4/channel_name=5:
  the source channel/topic NAMES ride the deliver/steer control op so the
  agent renders them without a roster lookup.
- PostMessageRequest.create_topic=6: the get-or-create gate (R5).

Go comms edge:
- store.ChannelByNameForViewer: viewer-scoped name→id resolve, sharing
  channelVisiblePredicate with ListChannels (no read/resolve drift).
  Unknown OR invisible name → ErrNotFound (D9 merge); ambiguous name →
  ErrInvalidArgument naming the collision (no ErrAmbiguous sentinel).
- TopicRef.Create gates get-or-create in resolveTopicForAppend: set → mint;
  unset → a name-miss is ErrNotFound, not a silent create (R5). Archived
  revival unchanged.
- comms_post_message / comms_post_ask route through the new
  PostAsAccountByName / ListAsAccountByName tool-arm wrappers, which resolve
  the channel NAME → id then delegate to the UNCHANGED id-typed
  PostAsAccount / ListAsAccount. Post/ask drop the home default (R2 — the
  agent must name its channel, even its own home); list keeps omit-=home
  (a read has no misroute hazard).
- defaultChannel: preserve CreateTopic on its home-channel request copy
  (latent bug the create_topic gate would have surfaced as a silent
  mint-failure on CommitAgentPost's frame-relay path).
- Internal producers that legitimately mint set Create=true (serve_seed
  Setup seed, CommitAgentPost frame relay, test helpers).

Go delivery denorm:
- store.TopicChannelNames(topicID) → (topicName, channelName): id-typed
  join, ErrNotFound on miss.
- Consumer.sourceNames mirrors authorHandle (empty topic id → empty; store
  miss → log + empty, never blocks delivery); deliverOp/steerOp carry
  ChannelName+TopicName on both DeliverControl and SteerControl.

TS agent tools:
- comms_post_message / comms_post_ask require an explicit channel NAME
  (home default dropped) + optional create_topic; comms_list_messages flips
  to a channel NAME param, keeps omit-=home. The name rides the existing
  container channelId arm (the Go edge resolves).
- formatDeliversForPrompt renders "Channel <name> › topic <name>:" + a reply
  cue naming both required post params; source names plumbed per message via
  #deliverSourceNames, mirroring #deliverFromHandles. Resolve misses degrade
  (topic → id, channel → placeholder).

Spec-impact: none. The name-addressing invariant (agents/UIs address by name; wire + store stay id-typed) is already recorded in the frozen design record and DL-291..297; this is its T0 implementation.

Refs RIG-2956

Co-authored-by: Matt Wilkinson <matt@rigel.build>
rigel-mintaka added a commit that referenced this pull request Aug 30, 2026
…nels+topics by name (RIG-2956)

The T0 slice of the peer-DM name-addressing cutover (frozen record
docs/designs/product/compass-agent-peer-dm/design.md §T0). Agents and UIs
address channels and topics by NAME; the wire and store stay id-typed,
resolved at the service edge (mirrors the merged handle-addressing cutover
#698 / DL-270). Proto denorm fields + Go edge + delivery denorm + TS tools,
one atomic change.

Proto (regen committed):
- DeliverControl.channel_name=5, SteerControl.topic_name=4/channel_name=5:
  the source channel/topic NAMES ride the deliver/steer control op so the
  agent renders them without a roster lookup.
- PostMessageRequest.create_topic=6: the get-or-create gate (R5).

Go comms edge:
- store.ChannelByNameForViewer: viewer-scoped name→id resolve, sharing
  channelVisiblePredicate with ListChannels (no read/resolve drift).
  Unknown OR invisible name → ErrNotFound (D9 merge); ambiguous name →
  ErrInvalidArgument naming the collision (no ErrAmbiguous sentinel).
- TopicRef.Create gates get-or-create in resolveTopicForAppend: set → mint;
  unset → a name-miss is ErrNotFound, not a silent create (R5). Archived
  revival unchanged.
- comms_post_message / comms_post_ask route through the new
  PostAsAccountByName / ListAsAccountByName tool-arm wrappers, which resolve
  the channel NAME → id then delegate to the UNCHANGED id-typed
  PostAsAccount / ListAsAccount. Post/ask drop the home default (R2 — the
  agent must name its channel, even its own home); list keeps omit-=home
  (a read has no misroute hazard).
- defaultChannel: preserve CreateTopic on its home-channel request copy
  (latent bug the create_topic gate would have surfaced as a silent
  mint-failure on CommitAgentPost's frame-relay path).
- Internal producers that legitimately mint set Create=true (serve_seed
  Setup seed, CommitAgentPost frame relay, test helpers).

Go delivery denorm:
- store.TopicChannelNames(topicID) → (topicName, channelName): id-typed
  join, ErrNotFound on miss.
- Consumer.sourceNames mirrors authorHandle (empty topic id → empty; store
  miss → log + empty, never blocks delivery); deliverOp/steerOp carry
  ChannelName+TopicName on both DeliverControl and SteerControl.

TS agent tools:
- comms_post_message / comms_post_ask require an explicit channel NAME
  (home default dropped) + optional create_topic; comms_list_messages flips
  to a channel NAME param, keeps omit-=home. The name rides the existing
  container channelId arm (the Go edge resolves).
- formatDeliversForPrompt renders "Channel <name> › topic <name>:" + a reply
  cue naming both required post params; source names plumbed per message via
  #deliverSourceNames, mirroring #deliverFromHandles. Resolve misses degrade
  (topic → id, channel → placeholder).

Spec-impact: none. The name-addressing invariant (agents/UIs address by name; wire + store stay id-typed) is already recorded in the frozen design record and DL-291..297; this is its T0 implementation.

Refs RIG-2956

Co-authored-by: Matt Wilkinson <matt@rigel.build>
rigel-mintaka added a commit that referenced this pull request Aug 30, 2026
…nels+topics by name (RIG-2956)

The T0 slice of the peer-DM name-addressing cutover (frozen record
docs/designs/product/compass-agent-peer-dm/design.md §T0). Agents and UIs
address channels and topics by NAME; the wire and store stay id-typed,
resolved at the service edge (mirrors the merged handle-addressing cutover
#698 / DL-270). Proto denorm fields + Go edge + delivery denorm + TS tools,
one atomic change.

Proto (regen committed):
- DeliverControl.channel_name=5, SteerControl.topic_name=4/channel_name=5:
  the source channel/topic NAMES ride the deliver/steer control op so the
  agent renders them without a roster lookup.
- PostMessageRequest.create_topic=6: the get-or-create gate (R5).

Go comms edge:
- store.ChannelByNameForViewer: viewer-scoped name→id resolve, sharing
  channelVisiblePredicate with ListChannels (no read/resolve drift).
  Unknown OR invisible name → ErrNotFound (D9 merge); ambiguous name →
  ErrInvalidArgument naming the collision (no ErrAmbiguous sentinel).
- TopicRef.Create gates get-or-create in resolveTopicForAppend: set → mint;
  unset → a name-miss is ErrNotFound, not a silent create (R5). Archived
  revival unchanged.
- comms_post_message / comms_post_ask route through the new
  PostAsAccountByName / ListAsAccountByName tool-arm wrappers, which resolve
  the channel NAME → id then delegate to the UNCHANGED id-typed
  PostAsAccount / ListAsAccount. Post/ask drop the home default (R2 — the
  agent must name its channel, even its own home); list keeps omit-=home
  (a read has no misroute hazard).
- defaultChannel: preserve CreateTopic on its home-channel request copy
  (latent bug the create_topic gate would have surfaced as a silent
  mint-failure on CommitAgentPost's frame-relay path).
- Internal producers that legitimately mint set Create=true (serve_seed
  Setup seed, CommitAgentPost frame relay, test helpers).

Go delivery denorm:
- store.TopicChannelNames(topicID) → (topicName, channelName): id-typed
  join, ErrNotFound on miss.
- Consumer.sourceNames mirrors authorHandle (empty topic id → empty; store
  miss → log + empty, never blocks delivery); deliverOp/steerOp carry
  ChannelName+TopicName on both DeliverControl and SteerControl.

TS agent tools:
- comms_post_message / comms_post_ask require an explicit channel NAME
  (home default dropped) + optional create_topic; comms_list_messages flips
  to a channel NAME param, keeps omit-=home. The name rides the existing
  container channelId arm (the Go edge resolves).
- formatDeliversForPrompt renders "Channel <name> › topic <name>:" + a reply
  cue naming both required post params; source names plumbed per message via
  #deliverSourceNames, mirroring #deliverFromHandles. Resolve misses degrade
  (topic → id, channel → placeholder).

Spec-impact: none. The name-addressing invariant (agents/UIs address by name; wire + store stay id-typed) is already recorded in the frozen design record and DL-291..297; this is its T0 implementation.

Refs RIG-2956

Co-authored-by: Matt Wilkinson <matt@rigel.build>
mattwilkinsonn added a commit that referenced this pull request Aug 30, 2026
…nels+topics by name (RIG-2956) (#730)

The T0 slice of the peer-DM name-addressing cutover (frozen record
docs/designs/product/compass-agent-peer-dm/design.md §T0). Agents and UIs
address channels and topics by NAME; the wire and store stay id-typed,
resolved at the service edge (mirrors the merged handle-addressing cutover
#698 / DL-270). Proto denorm fields + Go edge + delivery denorm + TS tools,
one atomic change.

Proto (regen committed):
- DeliverControl.channel_name=5, SteerControl.topic_name=4/channel_name=5:
  the source channel/topic NAMES ride the deliver/steer control op so the
  agent renders them without a roster lookup.
- PostMessageRequest.create_topic=6: the get-or-create gate (R5).

Go comms edge:
- store.ChannelByNameForViewer: viewer-scoped name→id resolve, sharing
  channelVisiblePredicate with ListChannels (no read/resolve drift).
  Unknown OR invisible name → ErrNotFound (D9 merge); ambiguous name →
  ErrInvalidArgument naming the collision (no ErrAmbiguous sentinel).
- TopicRef.Create gates get-or-create in resolveTopicForAppend: set → mint;
  unset → a name-miss is ErrNotFound, not a silent create (R5). Archived
  revival unchanged.
- comms_post_message / comms_post_ask route through the new
  PostAsAccountByName / ListAsAccountByName tool-arm wrappers, which resolve
  the channel NAME → id then delegate to the UNCHANGED id-typed
  PostAsAccount / ListAsAccount. Post/ask drop the home default (R2 — the
  agent must name its channel, even its own home); list keeps omit-=home
  (a read has no misroute hazard).
- defaultChannel: preserve CreateTopic on its home-channel request copy
  (latent bug the create_topic gate would have surfaced as a silent
  mint-failure on CommitAgentPost's frame-relay path).
- Internal producers that legitimately mint set Create=true (serve_seed
  Setup seed, CommitAgentPost frame relay, test helpers).

Go delivery denorm:
- store.TopicChannelNames(topicID) → (topicName, channelName): id-typed
  join, ErrNotFound on miss.
- Consumer.sourceNames mirrors authorHandle (empty topic id → empty; store
  miss → log + empty, never blocks delivery); deliverOp/steerOp carry
  ChannelName+TopicName on both DeliverControl and SteerControl.

TS agent tools:
- comms_post_message / comms_post_ask require an explicit channel NAME
  (home default dropped) + optional create_topic; comms_list_messages flips
  to a channel NAME param, keeps omit-=home. The name rides the existing
  container channelId arm (the Go edge resolves).
- formatDeliversForPrompt renders "Channel <name> › topic <name>:" + a reply
  cue naming both required post params; source names plumbed per message via
  #deliverSourceNames, mirroring #deliverFromHandles. Resolve misses degrade
  (topic → id, channel → placeholder).

Spec-impact: none. The name-addressing invariant (agents/UIs address by name; wire + store stay id-typed) is already recorded in the frozen design record and DL-291..297; this is its T0 implementation.

Refs RIG-2956

Co-authored-by: Matt Wilkinson <matt@rigel.build>
mattwilkinsonn added a commit that referenced this pull request Aug 30, 2026
… (RIG-2882) (#723)

RIG-2880 (#698) flipped the request-side comms fields from account-ids to
`@handle` inputs, renaming `GetRosterRequest.agent_account_id` -> `vantage_handle`.
The `fetchSnapshot` comment still named the old `agent_account_id` field twice
while describing the current wire contract; retarget it at `vantage_handle`.

Code was already correct (the call site sends only `{ scope }`, leaving the
handle field empty). This is the T7 stale-commentary item from the frozen
handle-addressing cutover record. Sweep-confirm + suite-run (1029/0 against the
regenerated client) recorded on RIG-2882.

Co-authored-by: Matt Wilkinson <matt@rigel.build>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants